#include "usbd_core.h"
#include "usbd_cdc.h"
#include <stm32f10x.h>
#include "ano_usb.h"
#include "AnoPTv8ExAPI.h"
/*!< endpoint address */
#define CDC_IN_EP 0x81
#define CDC_OUT_EP 0x01
#define CDC_INT_EP 0x85

#define USBD_VID 0x0483
#define USBD_PID 0xA0D0
#define USBD_MAX_POWER 100
#define USBD_LANGID_STRING 1033

/*!< config descriptor size */
#define USB_CONFIG_SIZE (9 + CDC_ACM_DESCRIPTOR_LEN * 1)

/*!< global descriptor */
static const uint8_t cdc_descriptor[] = {USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01),
                                         USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
                                         CDC_ACM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, 0x02),
                                         ///////////////////////////////////////
                                         /// string0 descriptor
                                         ///////////////////////////////////////
                                         USB_LANGID_INIT(USBD_LANGID_STRING),
                                         ///////////////////////////////////////
                                         /// string1 descriptor
                                         ///////////////////////////////////////
                                         12,                         /* bLength */
                                         USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
                                         'A', 0x00,                  /* wcChar0 */
                                         'N', 0x00,                  /* wcChar1 */
                                         'O', 0x00,                  /* wcChar2 */
                                         'T', 0x00,                  /* wcChar3 */
                                         'C', 0x00,                  /* wcChar4 */
                                         ///////////////////////////////////////
                                         /// string2 descriptor
                                         ///////////////////////////////////////
                                         0x24,                       /* bLength */
                                         USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
                                         'A', 0x00,                  /* wcChar0 */
                                         'n', 0x00,                  /* wcChar1 */
                                         'o', 0x00,                  /* wcChar2 */
                                         'P', 0x00,                  /* wcChar3 */
                                         'M', 0x00,                  /* wcChar4 */
                                         'U', 0x00,                  /* wcChar5 */
                                         ' ', 0x00,                  /* wcChar6 */
                                         ' ', 0x00,                  /* wcChar7 */
                                         ' ', 0x00,                  /* wcChar8 */
                                         ' ', 0x00,                  /* wcChar9 */
                                         ' ', 0x00,                  /* wcChar10 */
                                         ' ', 0x00,                  /* wcChar11 */
                                         ' ', 0x00,                  /* wcChar12 */
                                         'v', 0x00,                  /* wcChar13 */
                                         '1', 0x00,                  /* wcChar14 */
                                         '0', 0x00,                  /* wcChar15 */
                                         '0', 0x00,                  /* wcChar16 */
                                         ///////////////////////////////////////
                                         /// string3 descriptor
                                         ///////////////////////////////////////
                                         10,                         /* bLength */
                                         USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
                                         '2', 0x00,                  /* wcChar0 */
                                         '0', 0x00,                  /* wcChar1 */
                                         '2', 0x00,                  /* wcChar2 */
                                         '5', 0x00,                  /* wcChar3 */
#ifdef CONFIG_USB_HS
                                         ///////////////////////////////////////
                                         /// device qualifier descriptor
                                         ///////////////////////////////////////
                                         0x0a, USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, 0x00, 0x02, 0x02, 0x02, 0x01, 0x40, 0x01, 0x00,
#endif
                                         0x00};

#define HWCDCBUFLEN 64
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t cdcReadBuf[HWCDCBUFLEN];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t cdcSendBuf[HWCDCBUFLEN];
volatile bool ep_tx_busy_flag = false;

#ifdef CONFIG_USB_HS
#define CDC_MAX_MPS 512
#else
#define CDC_MAX_MPS 64
#endif

void usbd_configure_done_callback(void) {
    /* setup first out ep read transfer */
    usbd_ep_start_read(CDC_OUT_EP, cdcReadBuf, HWCDCBUFLEN);
}

void usbd_cdc_acm_bulk_out(uint8_t ep, uint32_t nbytes) {
    for (int i = 0; i < nbytes; i++) AnoPTv8HwRecvByte(LT_USBCDC, cdcReadBuf[i]);
    /* setup next out ep read transfer */
    usbd_ep_start_read(ep, cdcReadBuf, HWCDCBUFLEN);
}

void usbd_cdc_acm_bulk_in(uint8_t ep, uint32_t nbytes) {
    if ((nbytes % CDC_MAX_MPS) == 0 && nbytes) {
        /* send zlp */
        usbd_ep_start_write(ep, NULL, 0);
    } else {
        ep_tx_busy_flag = false;
    }
}

struct usbd_endpoint cdc_out_ep1 = {.ep_addr = CDC_OUT_EP, .ep_cb = usbd_cdc_acm_bulk_out};

struct usbd_endpoint cdc_in_ep1 = {.ep_addr = CDC_IN_EP, .ep_cb = usbd_cdc_acm_bulk_in};

struct usbd_interface intf0;
struct usbd_interface intf1;

void usb_bsp_inti(void) {
    RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
    RCC->APB1ENR |= RCC_APB1ENR_USBEN;  // enable clock for USB
    NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
}

void cdc_acm_multi_init(void) {
    usb_bsp_inti();

    usbd_desc_register(cdc_descriptor);

    usbd_add_interface(usbd_cdc_acm_init_intf(&intf0));
    usbd_add_interface(usbd_cdc_acm_init_intf(&intf1));
    usbd_add_endpoint(&cdc_out_ep1);
    usbd_add_endpoint(&cdc_in_ep1);

    usbd_initialize();
}

#define CDCTXBUFLEN 512
uint8_t CdcTxDataBuf[CDCTXBUFLEN];
uint16_t CdnTxDataBufInIndex = 0;
uint16_t CdnTxDataBufOutIndex = 0;
void DrvUsbCdcAddTxData(const uint8_t* buf, const uint16_t len) {
    for (int i = 0; i < len; i++) {
        CdcTxDataBuf[CdnTxDataBufInIndex++] = *(buf + i);
        if (CdnTxDataBufInIndex >= CDCTXBUFLEN) CdnTxDataBufInIndex = 0;
    }
}

void DrvUsbRunTask1MS(void) {
    if (CdnTxDataBufInIndex != CdnTxDataBufOutIndex) {
        uint16_t _dlen = 0;
        if (1) {
            if (CdnTxDataBufInIndex > CdnTxDataBufOutIndex)
                _dlen = CdnTxDataBufInIndex - CdnTxDataBufOutIndex;
            else
                _dlen = CDCTXBUFLEN - CdnTxDataBufOutIndex + CdnTxDataBufInIndex;
            if (HWCDCBUFLEN < _dlen) _dlen = HWCDCBUFLEN;
            ep_tx_busy_flag = true;
            for (int i = 0; i < _dlen; i++) {
                cdcSendBuf[i] = CdcTxDataBuf[CdnTxDataBufOutIndex++];
                if (CdnTxDataBufOutIndex >= CDCTXBUFLEN) CdnTxDataBufOutIndex = 0;
            }
            usbd_ep_start_write(CDC_IN_EP, cdcSendBuf, _dlen);
        }
    }
}
